#include "CPanel.h"
#include <cstdlib>
#include <iostream>
#include <cmath>

using namespace std;

//constructor
CPanel::CPanel()
{
	//default setting
	ntype = -1;
	Vindex = parent = left = right = -1;
	basis_index = -1;
	cut_flag = false;
}

//deconstructor
CPanel::~CPanel()
{}

//assignment
void CPanel::Assign(CPanel &Ai) const
{
	for(int i=0; i<4; i++)
	{
		Ai.index[i] = index[i];
		Ai.side[i] = side[i];
	}

	Ai.cond_number = cond_number;
	Ai.shape = shape;
	Ai.root = root;
	Ai.cut_flag = cut_flag;
}

//find center of panel
void CPanel::FindCenter()
{
	center = (index[0]+index[1]+index[2]+index[3])*0.25;
}

//find area of panel
void CPanel::FindArea()
{
	//rectangle only now, I'll add triangle case later
	double side01, side12, side23, side30, side02;
	side01 = index[0].abs(index[1]);
	side12 = index[1].abs(index[2]);
	side23 = index[2].abs(index[3]);
	side30 = index[3].abs(index[0]);
	side02 = index[0].abs(index[2]);
	area = Heron(side01, side12, side02) + Heron(side23, side30, side02);
}

//Heron method to compute the area
double CPanel::Heron(const double side1, const double side2, const double side3)
{
	double s = 0.5*(side1 + side2 + side3);
	return sqrt(s*(s-side1)*(s-side2)*(s-side3));
}

//get the longest side value
double CPanel::GetLongestSideValue() const
{
	double side[4];
	side[0] = index[0].abs(index[1]);
	side[1] = index[1].abs(index[2]);
	side[2] = index[2].abs(index[3]);
	side[3] = index[3].abs(index[0]);

	return side[GetLongestSideIndex()];
}

//get the longest side index
int CPanel::GetLongestSideIndex() const
{
	double side01, side12, side23, side30;
	side01 = index[0].abs(index[1]);
	side12 = index[1].abs(index[2]);
	side23 = index[2].abs(index[3]);
	side30 = index[3].abs(index[0]);

	if(side01>=side12 && side01>=side23 && side01>=side30)
		return 0;
	else if(side12>=side01 && side12>=side23 && side12>=side30)
		return 1;
	else if(side23>=side01 && side23>=side12 && side23>=side30)
		return 2;
	else 
		return 3;
}

//get the shortest side value
double CPanel::GetShortestSideValue() const
{
	double side01, side12, side23, side30, side02;
	side01 = index[0].abs(index[1]);
	side12 = index[1].abs(index[2]);
	side23 = index[2].abs(index[3]);
	side30 = index[3].abs(index[0]);
	side02 = index[0].abs(index[2]);

	if(side01<=side12 && side01<=side23 && side01<=side30)
		return side01;
	else if(side12<=side01 && side12<=side23 && side12<=side30)
		return side12;
	else if(side23<=side01 && side23<=side12 && side23<=side30)
		return side23;
	else 
		return side30;
}

//get the center of panel
C3DPoint CPanel::GetCenter() const
{ return center; }

//get the potential between 2 panels
double CPanel::GetPotential(const CPanel &Ai) const
{
	if(Vindex != Ai.Vindex)
	{
		double r = center.abs(Ai.GetCenter());
		if(r == 0)
		{
			cout<<"Potential is wrong!"<<endl;
			cout<<"panel["<<Vindex<<"] and panel["<<Ai.Vindex<<"] have the same center!!"<<endl;
			exit(0);
		}
		else
			return 1.0/(r);
	}
	else
	{
	/*	double l = GetLongestSideValue();
		double s = GetShortestSideValue();
		double r = l/s;
		int n = static_cast<int>((r-1.0)/2.0);

		double p = 3.53/l + (4.0*(r-2.0*n-1.0)/(r+2.0*n+1.0))/s;
		for(int i=1; i<n; i++)
			p += 2.0/(i*l);

		return p;
	*/	return 3.53/sqrt(area);
	}
}

//get the condNum
unsigned int CPanel::GetCondNum() const
{ return cond_number; }

//get left or right child
unsigned int CPanel::GetLeft() const
{ return left; }

unsigned int CPanel::GetRight() const
{ return right; }

//get parent
unsigned int CPanel::GetParent() const
{ return parent; }

//get root index
unsigned int CPanel::GetRoot() const
{ return root; }

//return basis index
unsigned int CPanel::GetBasisIndex() const
{ return basis_index; }

//get node type
int CPanel::GetNodeType() const
{
	if(ntype == -1)
	{
		cout<<"Node type error!!"<<endl;
		exit(0);
	}
	else return ntype;
}

//input the panel information
void CPanel::InputPanel(const int Pshape, const unsigned int condNum, const double Coord[], const unsigned int panelNum)
{
	if(Pshape == 0)
	{
		shape = Rectangle;
		cond_number = condNum;
		
		for(int i=0; i<4; i++)
		{
			index[i].Set(Coord[i*3], Coord[i*3+1], Coord[i*3+2]);
			side[i] = true;
		}
		
		FindCenter();
		FindArea();
		
		ntype = 0;	//0 means root
		Vindex = root = basis_index = panelNum;
		parent = -1;
		cut_flag = false;
	}
	else
	{
		//triangle case
	}
}

//cutFlag
bool CPanel::isCut() const
{ return cut_flag; }

//panel partition
void CPanel::PanelPartition(vector<CPanel*> &panelList, unsigned int &panelNum, int &basisNum)
{
	CPanel *Ai1 = new CPanel;
	CPanel *Ai2 = new CPanel;

	Assign(*Ai1);
	Assign(*Ai2);

	int LRIndex = GetLongestSideIndex();	//Longest side index

	if(shape == Rectangle)	//rectangle case
	{
		if(LRIndex == 0 || LRIndex == 2)	//set new index coordinate
		{
			Ai2->index[0] = Ai1->index[1] = (index[0]+index[1])*0.5;
			Ai2->index[3] = Ai1->index[2] = (index[2]+index[3])*0.5;
			Ai1->side[1] = Ai2->side[3] = false;
		}
		else
		{
			Ai2->index[1] = Ai1->index[2] = (index[1]+index[2])*0.5;
			Ai2->index[0] = Ai1->index[3] = (index[0]+index[3])*0.5;
			Ai1->side[2] = Ai2->side[0] = false;
		}
	}
	else
	{
		//triangle case
	}
	
	//define Ai1 and Ai2 parameters
	Ai1->FindCenter();
	Ai2->FindCenter();
	Ai1->FindArea();
	Ai2->FindArea();
	
	Ai1->parent = Ai2->parent = Vindex;
	left = Ai1->Vindex = panelNum++;
	right = Ai2->Vindex = panelNum++;
	Ai1->ntype = 1;
	Ai2->ntype = 2;
	Ai1->basis_index = basisNum++;
	Ai2->basis_index = -1;
			
	cut_flag = true;
	
	panelList.push_back(Ai1);
	panelList.push_back(Ai2);
}

bool CPanel::Decision(const CPanel &A, const double *err)
{
	C3DPoint p1, p2;
	C3DPoint left_center, right_center;
	int LRIndex = GetLongestSideIndex();

	if(LRIndex == 0 || LRIndex == 2)
	{
		p1 = (index[0]+index[1])*0.5;
		p2 = (index[2]+index[3])*0.5;
		left_center = (p1+p2+index[0]+index[3])*0.25;
		right_center = (p1+p2+index[1]+index[2])*0.25;
	}
	else
	{
		p1 = (index[0]+index[3])*0.5;
		p2 = (index[1]+index[2])*0.5;
		left_center = (p1+p2+index[0]+index[1])*0.25;
		right_center = (p1+p2+index[2]+index[3])*0.25;
	}

	double r1 = left_center.abs(A.center);
	double r2 = right_center.abs(A.center);
	double r = center.abs(A.center);

	double rMax = (r1>r2)?r1:r2;
	double rMin = (r2>r1)?r1:r2;

	if(rMin > r)
		if(r > (1-err[0])*rMax)
			return true;
		else
			return false;
	else
		if(r < (1+err[1])*rMin && r > (1-err[1])*rMax)
			return true;
		else
			return false;

}

/*** return panel vertex location **/
C3DPoint CPanel::GetPanelVertex(const int Pindex) const
{
	return index[Pindex];
}

